/* **********************************************************************
 *
 * Fixed Point Math Library
 *
 * **********************************************************************
 *
 * fixed.h
 *
 * Alex Forencich
 * alex@alexelectronics.com
 *
 * **********************************************************************/

#ifndef _FIXED_H_
#define _FIXED_H_

/*
 * Includes
 */

/*
 * Defines
 */

/*
 * Constants
 */

// Fractional Bits
// pick *one*
// recommended formats: 24.8, 16.16, 8.24, and 0.32

// 24.8
#define	FIXED_FRACBITS	20
// 16.16
//#define	FIXED_FRACBITS	16
// 8.24
//#define	FIXED_FRACBITS	24
// 0.32
//#define	FIXED_FRACBITS	32

#define FIXED_RESOLUTION (1 << FIXED_FRACBITS)

#define FIXED_INT_MASK (0xffffffffL << FIXED_FRACBITS)
#define FIXED_FRAC_MASK (~FIXED_INT_MASK)

// square roots
#define FIXED_SQRT_ERR 1 << (0)
//#define FIXED_SQRT_ERR 1 << (16 - 10)
//#define FIXED_SQRT_ERR 1 << (24 - 10)
//#define FIXED_SQRT_ERR 1 << (32 - 10)

// fixedp2a
#define FIXED_DECIMALDIGITS 6

/*
 * Typedefs
 */
typedef long fixedp;

/*
 * Globals
 */

/*
 * Macros
 */

// basic math
#define qadd(a, b) ((a) + (b))
#define qsub(a, b) ((a) - (b))

// conversions for arbitrary fracbits
#define _short2q(x, fb)			((fixedp)((x) << (fb)))
#define _int2q(x, fb)			((fixedp)((x) << (fb)))
#define _long2q(x, fb)			((fixedp)((x) << (fb)))
#define _float2q(x, fb)			((fixedp)((x) * (1 << (fb))))
#define _double2q(x, fb)		((fixedp)((x) * (1 << (fb))))

// conversions for global fixed point type
#define short2q(x)			_short2q(x, FIXED_FRACBITS)
#define int2q(x)			_int2q(x, FIXED_FRACBITS)
#define long2q(x)			_long2q(x, FIXED_FRACBITS)
#define float2q(x)			_float2q(x, FIXED_FRACBITS)
#define double2q(x)			_double2q(x, FIXED_FRACBITS)

// conversions for arbitrary fracbits
#define _q2short(x, fb)		((short)((x) >> (fb)))
#define _q2int(x, fb)		((int)((x) >> (fb)))
#define _q2long(x, fb)		((long)((x) >> (fb)))
#define _q2float(x, fb)		((float)(x) / (1 << (fb)))
#define _q2double(x, fb)	((double)(x) / (1 << (fb)))

// conversions for global fixed point type
#define q2short(x)			_q2short(x, FIXED_FRACBITS)
#define q2int(x)			_q2int(x, FIXED_FRACBITS)
#define q2long(x)			_q2long(x, FIXED_FRACBITS)
#define q2float(x)			_q2float(x, FIXED_FRACBITS)
#define q2double(x)			_q2double(x, FIXED_FRACBITS)

#define qipart(x)			q2long(x)
#define qfpart(x)			((x) & FIXED_FRAC_MASK)
//#define qfpart(x)			((x) - ((x) & FIXED_INT_MASK))

/*
 * Constants
 */
#define _XPI      3.1415926535897932384626433832795
#define XPI      double2q(_XPI)
#define _X2PI     6.283185307179586476925286766559
#define X2PI     double2q(_X2PI)
#define _XPIO2    1.5707963267948966192313216916398
#define XPIO2    double2q(_XPIO2)
#define _XPIO4    0.78539816339744830961566084581988
#define XPIO4    double2q(_XPIO4)
#define _XLN_E    2.71828182845904523536
#define XLN_E    double2q(_XLN_E)
#define _XLN_10   2.30258509299404568402
#define XLN_10   double2q(_XLN_10)
#define _X1OLN_10 0.43429448190325182765
#define X1OLN_10 double2q(_X1OLN_10)


/*
 * Method Prototypes
 */
 
/**
 * convert one fixed point type to another
 * x - the fixed point number to convert
 * xFb - x's fracbits
 * yFb - the destination fracbits
 */
extern fixedp q2q(fixedp x, int xFb, int yFb);

/**
 * multiply two fixed point numbers with arbitrary fracbits
 * x - left operand
 * y - right operand
 * xFb - number of fracbits for X
 * yFb - number of fracbits for Y
 * resFb - number of fracbits for the result
 * 
 * Note that resFb must be greater than or equal to (xFb + yFb)
 * or else the result will be 0.
 */
extern fixedp _qmul(fixedp x, fixedp y, int xFb, int yFb, int resFb);

/**
 * multiply
 */
extern fixedp qmul(fixedp x, fixedp y);

/**
 * divide
 */
extern fixedp qdiv(fixedp x, fixedp y);

/**
 * inverse (x^-1) for arbitrary precision
 */
extern fixedp _qinv(fixedp x, int xFb, int resFb);


/**
 * inverse (x^-1)
 */
extern fixedp qinv(fixedp x);

/**
 * modulus
 */
extern fixedp qmod(fixedp x, fixedp y);

/**
 * absolute value
 */
extern fixedp qabs(register fixedp x);

/**
 * sign
 */
extern fixedp qsign(register fixedp x);

/**
 * multiply by sign
 * returns x * sign(s)
 */
extern fixedp qmulBySign(register fixedp x, register fixedp s);

/**
 * floor
 */
extern fixedp qfloor(fixedp x);

/**
 * ceil
 */
extern fixedp qceil(fixedp x);

/**
 * square root
 */
extern fixedp qsqrt(fixedp p_Square);

/**
 * log (base e)
 */
extern fixedp qlog( fixedp p_Base );

/**
 * log base 10
 */
extern fixedp qlog10( fixedp p_Base );

/**
 * exp (e to the x)
 */
extern fixedp qexp(fixedp p_Base);

/**
 * pow
 */
extern fixedp qpow( fixedp p_Base, fixedp p_Power );

/**
 * sine
 */
extern fixedp qsin(fixedp theta);

/**
 * cosine
 */
extern fixedp qcos(fixedp theta);

/**
 * tangent
 */
extern fixedp qtan(fixedp theta);

/**
 * fixedp2a - converts a fixed point number to a string
 */
extern char *q2a(char *buf, fixedp n);

#endif /* _FIXED_H_ */

